/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package edu.tue.nl.aa;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * 
 * 
 * @author waldo
 */
public class ExecuteAlgorithm {
    
    private static double LB1, LB2,
            GREEDY_TIME=0, ORDERED_TIME=0,
            P1C_TIME=0, P2C_TIME=0, P3C_TIME=0,
            PM2C_TIME=0, PMC_TIME=0;
    private static double MS_GREEDY=0, MS_ORDERED=0,
            MS_P1C=0, MS_P2C=0, MS_P3C=0,
            MS_PM2C=0, MS_PMC=0;
    private static long TIMES[][];
    private static double MAKESPANS[][];
    private static ArrayList<JobDTO> jobs;
    private static OrderedSchedulingAlgorithm greedy;
    private static  PkChoices pkc;
    private static OrderedSchedulingAlgorithm ordered;
    
    private static void loadJobs( File dataSet ){
        String line;
        jobs = new ArrayList<JobDTO>();
        LB1=0; LB2=0;
        long J=0;
        try {
            BufferedReader br = new BufferedReader(new FileReader(dataSet));
            while( (line = br.readLine()) != null ){
                J=Long.parseLong(line);
                LB1+=J;
                if( LB2<J ) LB2=J;
                jobs.add( new JobDTO( J ) );
            }
        } catch (IOException ex) {
            Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    /**
     * Example: /home/waldo/java/OrderedSchedulingAlgorithm/data/document/newExp/1M_jobs_1to500.csv 10 15
     * 1) 10 is the number of machines
     * 2) 15 is the number of repetitions for each algorithm
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception{
        // Load the data set and number of machines...
        File dataSet = new File(args[0]), results;
        int m = Integer.parseInt( args[1] ), w;
        int T = Integer.parseInt(args[2]);
        String resultsDir = args[0].split("\\.")[0] + "_" +m+"_machines";
        new File(resultsDir).mkdir();
        loadJobs( dataSet );
        Writer resultsWriter=null;
        long tl, i, makespan, startTime, endTime;
        // Specify the lower bound 1 (the average jobs time)
        LB1/=((double)m);
        StringBuilder s;
        
        TIMES = new long[7][T];
        MAKESPANS = new double[7][T];

        greedy = new OrderedSchedulingAlgorithm( (ArrayList<JobDTO>)jobs.clone(), m );
        greedy.doLoadBalancing( false, false );
        pkc = new PkChoices( (ArrayList<JobDTO>)jobs.clone(), m );
        pkc.doLoadBalancing( 2 );
        ordered = new OrderedSchedulingAlgorithm( (ArrayList<JobDTO>)jobs.clone(), m );
        ordered.doLoadBalancing( true, false );
        
      for( w=1;w<=T;w++ ){
        // Greedy LB object
        Thread.sleep(350);//wait for .35 seconds
        greedy = new OrderedSchedulingAlgorithm( (ArrayList<JobDTO>)jobs.clone(), m );
startTime = System.currentTimeMillis();
        greedy.doLoadBalancing( false, false );
endTime = System.currentTimeMillis();
        TIMES[0][w-1] = (endTime-startTime);
        GREEDY_TIME += TIMES[0][w-1];
        results = new File(resultsDir+"/greedy"+w+".txt");
        s = new StringBuilder();
        resultsWriter=null;
        try {
            resultsWriter = new BufferedWriter(new FileWriter(results));
            s.append("Greedy LB. It used a heap (PriorityQueue) to select the "); s.append(m);
            s.append(" machine for the distribution of each one of the "); s.append(jobs.size()); s.append(" jobs.\nIt took: ");
            s.append( TIMES[0][w-1] ); s.append(" milliseconds.\n");
            s.append( "Results:\n" );
            resultsWriter.write( s.toString() );
            Iterator it = greedy.getResult().iterator();
            i=1; tl=0;
            while( it.hasNext() ){
                s = new StringBuilder();
                MachineDTO machine = (MachineDTO)it.next();
                int j = machine.getAmountOfJobs();
                tl=machine.getTimeLoad();
                s.append("  Machine "); s.append(i); s.append(". timeload: "); s.append(tl);
                s.append(" with ");s.append(j); s.append(" jobs.\n");
                resultsWriter.write( s.toString() );
                i++;
            }
            s = new StringBuilder();
            s.append( "\n-----------------------------------------------------Details:\n" );
            resultsWriter.write( s.toString() );
            it = greedy.getResult().iterator();
            i=1; tl=0; makespan=0;
            while( it.hasNext() ){
                s = new StringBuilder();
                MachineDTO machine = (MachineDTO)it.next();
                int j = machine.getAmountOfJobs(), l=0;
                s.append("  Machine "); s.append(i); s.append(" has "); s.append(j); s.append(" jobs:\n    ");
                for( int k=0;k<j;k++ ){
                    s.append(machine.getJobTime(k)); s.append(", ");
                    l++;
                    if( l==10 ) { s.append("\n    "); l=0; }
                }
                tl=machine.getTimeLoad();
                if( makespan<tl ) makespan=tl;
                s.append("\n    Time load: "); s.append( tl ); s.append("\n");
                resultsWriter.write( s.toString() );
                i++;
            }
            MAKESPANS[0][w-1] = makespan;
            MS_GREEDY += MAKESPANS[0][w-1];
            s = new StringBuilder();
            s.append("\n\nMakespan: "); s.append(makespan);
            s.append("\nOPT=max(");s.append(LB1);s.append(",");s.append(LB2);s.append(")");
            resultsWriter.write( s.toString() );
            /*
            s = new StringBuilder();
            s.append("\n-----------------------------------------------------List of processing time of jobs:\n   ");
            resultsWriter.write( s.toString() );
            it = greedy.getJobs().iterator();
            i=0;
            s = new StringBuilder();
            while( it.hasNext() ){
                s.append( ((JobDTO)it.next()).getTime() ); s.append(", ");
                i++;
                if( i==10 ) { s.append("\n   "); i=0; }
            }
            resultsWriter.write( s.toString() );
            */
        } catch (IOException ex) {
            Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
        } finally {
            try {
                resultsWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
            }
        }
        
      for( int pk=1;pk<6;pk++ ){
        int km = pk;
        if( pk==4 ) km=m/2;
        if( pk==5 ) km=m;
        // Power of k choices object
        pkc = new PkChoices( (ArrayList<JobDTO>)jobs.clone(), m );
        Thread.sleep(350);//wait for .35 seconds
startTime = System.currentTimeMillis();
        pkc.doLoadBalancing( km );
endTime = System.currentTimeMillis();
        switch(pk){
            case 1: TIMES[1][w-1] = (endTime-startTime);
                P1C_TIME += TIMES[1][w-1];
                break;
            case 2: TIMES[2][w-1] = (endTime-startTime);
                P2C_TIME += TIMES[2][w-1];
                break;
            case 3: TIMES[3][w-1] = (endTime-startTime);
                P3C_TIME += TIMES[3][w-1];
                break;
            case 4: TIMES[4][w-1] = (endTime-startTime);
                PM2C_TIME += TIMES[4][w-1];
                break;
            default: TIMES[5][w-1] = (endTime-startTime);
                PMC_TIME += TIMES[5][w-1];
                break;
        }
        results = new File(resultsDir+"/P"+km+"C"+w+".txt");
        s = new StringBuilder();
        resultsWriter=null;
        try {
            resultsWriter = new BufferedWriter(new FileWriter(results));
            s.append("P");s.append(km);s.append("C LB. It used uniform random distribution to select ");
            s.append(km);s.append(" of the "); s.append(m);s.append(" machines for the distribution of each one of the ");
            s.append(jobs.size()); s.append(" jobs.\nIt took: "); s.append( (endTime-startTime) ); s.append(" milliseconds.\n");
            s.append( "Results:\n" );
            resultsWriter.write( s.toString() );
            Iterator it = pkc.getResult().iterator();
            i=1; tl=0;
            while( it.hasNext() ){
                s = new StringBuilder();
                MachineDTO machine = (MachineDTO)it.next();
                int j = machine.getAmountOfJobs();
                tl=machine.getTimeLoad();
                s.append("  Machine "); s.append(i); s.append(". timeload: "); s.append(tl);
                s.append(" with ");s.append(j); s.append(" jobs.\n");
                resultsWriter.write( s.toString() );
                i++;
            }
            s = new StringBuilder();
            s.append( "\n-----------------------------------------------------Details:\n" );
            resultsWriter.write( s.toString() );
            it = pkc.getResult().iterator();
            i=1; tl=0; makespan=0;
            while( it.hasNext() ){
                s = new StringBuilder();
                MachineDTO machine = (MachineDTO)it.next();
                int j = machine.getAmountOfJobs(), l=0;
                s.append("  Machine "); s.append(i); s.append(" has "); s.append(j); s.append(" jobs:\n    ");
                for( int k=0;k<j;k++ ){
                    s.append(machine.getJobTime(k)); s.append(", ");
                    l++;
                    if( l==10 ) { s.append("\n    "); l=0; }
                }
                tl=machine.getTimeLoad();
                if( makespan<tl ) makespan=tl;
                s.append("\n    Time load: "); s.append( tl ); s.append("\n");
                resultsWriter.write( s.toString() );
                i++;
            }
            switch(pk){
                case 1: MAKESPANS[1][w-1] = makespan;
                    MS_P1C += MAKESPANS[1][w-1];
                    break;
                case 2: MAKESPANS[2][w-1] = makespan;
                    MS_P2C += MAKESPANS[2][w-1];
                    break;
                case 3: MAKESPANS[3][w-1] = makespan;
                    MS_P3C += MAKESPANS[3][w-1];
                    break;
                case 4: MAKESPANS[4][w-1] = makespan;
                    MS_PM2C += MAKESPANS[4][w-1];
                    break;
                default: MAKESPANS[5][w-1] = makespan;
                    MS_PMC += MAKESPANS[5][w-1];
                    break;
            }
            s = new StringBuilder();
            s.append("\n\nMakespan: "); s.append(makespan);
            s.append("\nOPT=max(");s.append(LB1);s.append(",");s.append(LB2);s.append(")");
            resultsWriter.write( s.toString() );
            /*
            s = new StringBuilder();
            s.append("\n-----------------------------------------------------List of processing time of jobs:\n   ");
            resultsWriter.write( s.toString() );
            it = pkc.getJobs().iterator();
            i=0;
            s = new StringBuilder();
            while( it.hasNext() ){
                s.append( ((JobDTO)it.next()).getTime() ); s.append(", ");
                i++;
                if( i==10 ) { s.append("\n   "); i=0; }
            }
            resultsWriter.write( s.toString() );
            */
        } catch (IOException ex) {
            Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
        } finally {
            try {
                resultsWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
            }
        }
      }
        
        // Ordered LB with java.util.Collections.sort() object
        ordered = new OrderedSchedulingAlgorithm( (ArrayList<JobDTO>)jobs.clone(), m );
        Thread.sleep(350);//wait for .35 seconds
startTime = System.currentTimeMillis();
        ordered.doLoadBalancing( true, false );
endTime = System.currentTimeMillis();
        TIMES[6][w-1] = (endTime-startTime);
        ORDERED_TIME += TIMES[6][w-1];
        results = new File(resultsDir+"/orderedLB"+w+".txt");
        s = new StringBuilder();
        resultsWriter=null;
        try {
            resultsWriter = new BufferedWriter(new FileWriter(results));
            s.append("Ordered LB. It used java.util.Collections.sort() -mergesort- for the jobs and ");
            s.append("a heap (PriorityQueue) to select 1 of the ");s.append(m);
            s.append(" machines for the distribution of each one of the "); s.append(jobs.size());
            s.append(" jobs.\nIt took: "); s.append( TIMES[6][w-1] ); s.append(" milliseconds.\n");
            s.append( "Results:\n" );
            resultsWriter.write( s.toString() );
            Iterator it = ordered.getResult().iterator();
            i=1; tl=0;
            while( it.hasNext() ){
                s = new StringBuilder();
                MachineDTO machine = (MachineDTO)it.next();
                int j = machine.getAmountOfJobs();
                tl=machine.getTimeLoad();
                s.append("  Machine "); s.append(i); s.append(". timeload: "); s.append(tl);
                s.append(" with ");s.append(j); s.append(" jobs.\n");
                resultsWriter.write( s.toString() );
                i++;
            }
            s = new StringBuilder();
            s.append( "\n-----------------------------------------------------Details:\n" );
            resultsWriter.write( s.toString() );
            it = ordered.getResult().iterator();
            i=1; tl=0; makespan=0;
            while( it.hasNext() ){
                s = new StringBuilder();
                MachineDTO machine = (MachineDTO)it.next();
                int j = machine.getAmountOfJobs(), l=0;
                s.append("  Machine "); s.append(i); s.append(" has "); s.append(j); s.append(" jobs:\n    ");
                for( int k=0;k<j;k++ ){
                    s.append(machine.getJobTime(k)); s.append(", ");
                    l++;
                    if( l==10 ) { s.append("\n    "); l=0; }
                }
                tl=machine.getTimeLoad();
                if( makespan<tl ) makespan=tl;
                s.append("\n    Time load: "); s.append( tl ); s.append("\n");
                resultsWriter.write( s.toString() );
                i++;
            }
            MAKESPANS[6][w-1] = makespan;
            MS_ORDERED += MAKESPANS[6][w-1];
            s = new StringBuilder();
            s.append("\n\nMakespan: "); s.append(makespan);
            s.append("\nOPT=max(");s.append(LB1);s.append(",");s.append(LB2);s.append(")");
            resultsWriter.write( s.toString() );
            /*
            s = new StringBuilder();
            s.append("\n-----------------------------------------------------List of processing time of jobs:\n   ");
            resultsWriter.write( s.toString() );
            it = ordered.getJobs().iterator();
            i=0;
            s = new StringBuilder();
            while( it.hasNext() ){
                s.append( ((JobDTO)it.next()).getTime() ); s.append(", ");
                i++;
                if( i==10 ) { s.append("\n   "); i=0; }
            }
            resultsWriter.write( s.toString() );
            */
        } catch (IOException ex) {
            Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
        } finally {
            try {
                resultsWriter.close();
            } catch (IOException ex) {
                Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
            }
        }
      }
      
      GREEDY_TIME/=(double)T; ORDERED_TIME/=(double)T;
      P1C_TIME/=(double)T; P2C_TIME/=(double)T; P3C_TIME/=(double)T;
      PM2C_TIME/=(double)T; PMC_TIME/=(double)T;
      MS_GREEDY/=(double)T; MS_ORDERED/=(double)T;
      MS_P1C/=(double)T; MS_P2C/=(double)T; MS_P3C/=(double)T;
      MS_PM2C/=(double)T; MS_PMC/=(double)T;
      results = new File(resultsDir+"/averages.txt");
      s = new StringBuilder();
      resultsWriter=null;
      try {
          resultsWriter = new BufferedWriter(new FileWriter(results));
          s.append("Average running times (milliseconds) and makespans from the ");s.append(T);s.append(" repetitions:");
          s.append("\nGreedy: ");s.append(GREEDY_TIME);s.append(", makespan: ");s.append(MS_GREEDY);
          s.append("\nP1C: ");s.append(P1C_TIME);s.append(", makespan: ");s.append(MS_P1C);
          s.append("\nP2C: ");s.append(P2C_TIME);s.append(", makespan: ");s.append(MS_P2C);
          s.append("\nP3C: ");s.append(P3C_TIME);s.append(", makespan: ");s.append(MS_P3C);
          s.append("\nPM2C: ");s.append(PM2C_TIME);s.append(", makespan: ");s.append(MS_PM2C);
          s.append("\nPMC: ");s.append(PMC_TIME);s.append(", makespan: ");s.append(MS_PMC);
          s.append("\nOrdered: ");s.append(ORDERED_TIME);s.append(", makespan: ");s.append(MS_ORDERED);
          s.append("\nDetails (Times and Makespans)");
          resultsWriter.write( s.toString() );
          for( int z=0;z<7;z++){
              s = new StringBuilder();
              switch(z){
                case 0: s.append("\n-------------------------- Greedy:\n"); break;
                case 1: s.append("\n-------------------------- P1C:\n"); break;
                case 2: s.append("\n-------------------------- P2C:\n"); break;
                case 3: s.append("\n-------------------------- P3C:\n"); break;
                case 4: s.append("\n-------------------------- PM2C:\n"); break;
                case 5: s.append("\n-------------------------- PMC:\n"); break;
                default: s.append("\n-------------------------- Ordered:\n"); break;
              }
              for(w=1;w<=T;w++){s.append(TIMES[z][w-1]);s.append(",");}
              s.append("\n");
              for(w=1;w<=T;w++){s.append(MAKESPANS[z][w-1]);s.append(",");}
              resultsWriter.write( s.toString() );
          }
      } catch (IOException ex) {
          Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
      } finally {
          try {
              resultsWriter.close();
          } catch (IOException ex) {
              Logger.getLogger(ExecuteAlgorithm.class.getName()).log(Level.SEVERE, ex.toString(), ex);
          }
      }

    }
    
}
